Proxy
是一個 ES 6 引入的新物件,Proxy 一詞在中文中是「代理」的意思。
現實中的例子來舉例,比如老闆管理一家公司,日理萬機,可不是誰都能見到老闆的。
那由誰來決定哪些人可以見老闆?秘書這個職位就負責處理這些事情。秘書熟知老闆的行程,做事習慣,手上的工作等等,透過瞭解老闆的優先序,他能為老闆排出最適切的會面對象。
秘書就是 Proxy
。
秘書是一個中間人的角色,他並不能直接改變老闆,但可以對要到達老闆前的事情做一層攔截跟處理。
Proxy
在 JS 的宣告方式如下
const p = new Proxy(target, handler)
兩個參數分別是 target
和 handler
,target
用於傳入原始對象(要代理的物件,老闆),handler
用於定義代理的處理行為。
JS 在對物件的操作上,多有定義內部方法(Internal Methods)來處理,這些方法我們沒辦法透過方法的名稱來使用,而是透過對應規範的語法來使用。
如 obj.name
實際上是對 obj
呼叫了內部方法 [[Get]]
,傳入屬性名稱並期望獲得物件上對應屬性的值。
一般來說內部方法會用 [[]]
來表示,由於不可呼叫性,如果對內部方法的實踐有興趣,就需要直接查閱引擎的原始碼(如 V8),才能了解如何實踐的,或是閱讀 ECMAScript,會有對應的規範描述。
我們通常會更熟知表面的行為如何反應而不去關注過多的內部實踐。
提到內部方法是因為 handler
主要的攔截對象就是對目標物件的操作。
handler
用於攔截的方法包括以下幾種,都對應到各種內部方法。
這些攔截方法被稱為攔截器(trap),當對目標物件用了對應的的方法,handler
就會被觸發且處理:
apply()
new
in
,ownKeys()
的操作來看一個實際的例子:
//明星
const star = {
name: 'Big Star',
address: 'Moon Street',
phone: '987-654-3210',
nextConcert: '2024-10-06',
getNextConcert() {
return this.nextConcert;
}
};
//經紀人
const agentHandler = {
//攔截對明星資訊的訪問
get(target, property) {
//只有當詢問演唱會時間才會回答
if (property === 'nextConcert') {
return target.getNextConcert();ㄥ
} else {
//其他一概不知道
console.log(`Proxy get the request to : ${property} and block.`);
return undefined;
}
}
};
//使用 Proxy 創建經紀人
const agent = new Proxy(star, agentHandler);
console.log(agent.nextConcert); // "2024-10-06"
console.log(agent.address); //"Proxy get the request to : address and block." undefined
console.log(agent.phone); // "Proxy get the request to : phone and block." undefined
代理的攔截一般可以作用於幾種場景:
set
,如果出現非預期的資料可以調整後再 set
,或拋出錯誤。construct
,為建構參數塞入額外的東西,或是攔截 get
在值不存在的情況下返回特定值。Proxy
其中一個有名的實踐就是 Vue.js
(這裡指 Vue 3
以後的版本)。
有使用過 Vue.js
的人應該知道,Vue.js
是一套 JS 的框架,主要用於前端開發,MVVM 的架構讓開發者可以專注在操作後端的資料流,前端的元件就自動對應反應。
「改後端的資料,讓前端的元件自動對應反應」,聽起來是不是就像是代理的行為一樣?對,因為 Vue.js
就是為每個資料都建立對應的 Proxy
物件,攔截 get
與 set
行為,這樣一旦資料更新,他便能對對應的 DOM元件 或 JS物件,做定義的操作,從而達到響應式的畫面與程式。
Proxy
在對物件的監視、同步、攔截上都有很好效果,希望這篇能讓大家更熟悉這個 Proxy
的特性與使用範例。